package com.rankeiot.core;


import com.rankeiot.core.data.Pair;
import com.rankeiot.core.exception.AuthException;
import com.rankeiot.core.util.LocaleUtil;
import com.rankeiot.core.util.StringUtil;
import com.rankeiot.core.util.TokenUtil;
import com.rankeiot.core.data.UserInfo;


import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.web.context.request.*;


import java.util.function.BiFunction;


/**
 * 使用权限验证
 *
 */
@SuppressWarnings("AlibabaConstantFieldShouldBeUpperCase")
public final class Current {
    private static BiFunction<Character, String, UserInfo> userLoader;
    private static final String userKeyAttrName = "@user_key_";
    private static final String userAttrName = "@user_info_";
    private static final String REMOVE_TOKEN_HEADER = "rtk";
    public static final char TYPE_USER = 'U';
    public static final char TYPE_MEMBER = 'M';

    public static final char SUB_TYPE_DEFAULT = '_';

    //用户缓存，key:userToken={超时时间:用户信息}
    protected static Cache _allUsers;//<String, Pair<Long, UserInfo>> allUsers;
    private static int timeout = 1800;//s
    //还差500s过期
    private static int timeoutOffset = 500;

    public synchronized static Cache getCache() {
        if (_allUsers == null) {
            CacheManager cacheManager = Context.getBean(CacheManager.class);
            _allUsers = cacheManager.getCache("cacheUser");
        }
        return _allUsers;
    }

    public static void setUserLoader(BiFunction<Character, String, UserInfo> loader) {
        userLoader = loader;
    }

    public static String getUserToken(HttpServletRequest request) {
        String keyName = userKeyAttrName;
        if (request == null) {
            return null;
        }
        String userToken = (String) request.getAttribute(keyName);
        if (StringUtil.isEmpty(userToken)) {
            userToken = request.getHeader(getTokenName());
            if (StringUtil.isEmpty(userToken)) {
                userToken = request.getParameter("_token");// ctx.param("_token");
            }
            if (StringUtil.isNotEmpty(userToken)) {
                request.setAttribute(keyName, userToken);
            }
        }
        return userToken;
    }

    /**
     * 踢出全部用户。注意：虽然踢出了，但是用依然可以凭借token重新登录。
     */
    public static void invalidateAllUser() {
        getCache().clear();
    }

    //    /**
//     * 踢出用户,一般管理员修改信息后可通过踢出用户重新加载
//     * @param userName
//     */
//    public static void invalidateUser(String userName){
//        Cache cache=getCache();
//      //  cache.
//        for(Pair<Long,UserInfo> up:allUsers){
//            UserInfo u=up.getValue();
//            if(u!=null&& Objects.equals(userName,u.getUsername())){
//                allUsers.remove(u.getToken());
//            }
//        }
//    }
    public static void logout() {
        ServletRequestAttributes attributes = getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        HttpServletResponse response = attributes.getResponse();
        String token = getUserToken(request);
        if (StringUtil.isNotEmpty(token)) {
            getCache().evict(token);
            response.setHeader(REMOVE_TOKEN_HEADER, "true");
        }
    }

    public static UserInfo user() {
        return user(true);
    }

    public static UserInfo user(boolean autoLoad) {
        ServletRequestAttributes attributes = getRequestAttributes();
        if(attributes==null){
            return null;
        }
        Object user = attributes.getAttribute(userAttrName,RequestAttributes.SCOPE_REQUEST);
        if(user!=null){
            return (UserInfo) user;
        }
        HttpServletRequest request = attributes.getRequest();
        HttpServletResponse response = attributes.getResponse();
        String token = getUserToken(request);
        return user(autoLoad, token, request, response);
    }
    /**
     * 获取用信息
     *
     * @param autoLoad 自动根据token载入
     * @return
     */

    public static UserInfo user(boolean autoLoad, String token, HttpServletRequest request, HttpServletResponse response) {
        if (StringUtil.isEmpty(token)) {
            return null;
        }
        long t=TokenUtil.getTime(token);
        long now = System.currentTimeMillis() / 1000;
        //token如果超期无需验证
        if (now > t ) {
            return null;
        }

        Cache cache = getCache();
        String cacheKey=TokenUtil.getData(token);
        UserInfo cacheUser = cache.get(cacheKey, UserInfo.class);
        //没有缓存，去载入
        if (cacheUser == null) {
            return autoLoad ? loadUser(token) : null;
        }
        //有缓存，验证token
        if(TokenUtil.validate(token, cacheKey, cacheUser.getPassword())){
            cacheUser.setToken(token);
            return cacheUser;
        }
        return null;
    }

    /**
     * 获取用信息
     *
     * @param autoLoad 自动根据token载入
     * @return
     */
    /*
    public static UserInfo user(boolean autoLoad, String token, HttpServletRequest request, HttpServletResponse response) {

        if (StringUtil.isEmpty(token)) {
            return null;
        }
        Cache cache = getCache();
        String cacheKey=TokenUtil.getData(token);

        Pair<Long, UserInfo> cacheUser = cache.get(cacheKey, Pair.class);//allUsers.get(token, true);

        if (cacheUser == null) {
            if(request!=null){
                //切换token
                String perToken=request.getHeader("per_token");
                if(StringUtil.isNotEmpty(perToken)){
                    cacheUser=cache.get(perToken,Pair.class);
                    if(cacheUser!=null){
                        //切换token
                        UserInfo userInfo = cacheUser.getValue();
                        if(TokenUtil.validate(token, userInfo.getUserType()+userInfo.getUsername(), userInfo.getPassword())){
                            userInfo.setToken(token);
                            long time= TokenUtil.getTime(token);
                            cacheUser.setKey(time);
                            Cache allUsers=getCache();
                            allUsers.evict(perToken);
                            allUsers.put(token, cacheUser);
                            if(request!=null){
                                request.setAttribute(userAttrName,userInfo);
                            }
                            return userInfo;
                        }
                    }
                }
            }
            return autoLoad ? loadUser(token) : null;
        }
        long now = System.currentTimeMillis() / 1000;
        long t = cacheUser.getKey();
        UserInfo userInfo = cacheUser.getValue();
        //不足5分钟的时候更新token
        if (now > t - timeoutOffset) {
            String newTk = TokenUtil.token(userInfo.getUserType()+userInfo.getUsername(), userInfo.getPassword(), timeout);
            userInfo.setToken(newTk);
            if(request!=null){
                request.setAttribute(userKeyAttrName, newTk);
            }
            long timeExpired = System.currentTimeMillis() / 1000 + timeout;
            cacheUser.setKey(timeExpired);
            cacheUser.getValue().setToken(newTk);
            Cache allUsers=getCache();
            allUsers.evict(token);
            allUsers.put(newTk, cacheUser);
            if(response!=null){
                setToken(newTk, response);
            }
        }
        if(request!=null){
            request.setAttribute(userAttrName,userInfo);
        }
        return userInfo;
    }
    **/

    /**
     * 缓存中没有用户时,通过token加载用户数据
     * @param token
     * @return
     */
    private static UserInfo loadUser(String token) {
        if (userLoader == null) {
            return null;
        }
        String data = TokenUtil.getData(token);
        if (StringUtil.isNotEmpty(data)) {
            char userType = data.charAt(0);
            String userName = data.substring(1);
            UserInfo temp = userLoader.apply(userType, userName);
            if (TokenUtil.validate(token, userType + userName, temp.getPassword())) {
                Cache allUsers=getCache();
                allUsers.put(data,temp);
                temp.setToken(token);
                return temp;
            }else {
                throw new AuthException(LocaleUtil.get("token.overtime"));
            }
        }
        return null;
    }
    /**
    private static UserInfo loadUser(String token) {
        if (userLoader == null) {
            return null;
        }
        String data = TokenUtil.getData(token);
        if (StringUtil.isNotEmpty(data)) {
            char userType = data.charAt(0);
            String userName = data.substring(1);
            UserInfo temp = userLoader.apply(userType, userName);
            if (temp != null) {
                if (TokenUtil.validate(token, userType + userName, temp.getPassword())) {
                   long t=TokenUtil.getTime(token);
                    long now = System.currentTimeMillis() / 1000;
                    //如果即将超期
                    if (now > t - timeoutOffset) {
                        //重新登录并刷新token
                        login(temp);
                    }else{
                        //缓存token用户
                        temp.setToken(token);
                        Pair<Long, UserInfo> ud = new Pair<>(t, temp);
                        Cache allUsers=getCache();
                        allUsers.put(token,ud);
                    }
                    return temp;
                } else {
                   // Context.getBean()
                    throw new AuthException(LocaleUtil.get("token.overtime"));
                }
            }
        }
        return null;
    }
    **/
    /**
     * 登录，并刷新token
     *
     * @param userInfo
     */
    public static String login(UserInfo userInfo) {
        return login(userInfo, timeout);
    }

    /**
     * 登录
     *
     * @param userInfo
     * @param timeout  超时事件 s
     */
    public static String login(UserInfo userInfo, int timeout) {

        //无需续期
      //  String oldToken = userInfo.getToken();
        Cache allUsers=getCache();
//        if (StringUtil.isNotEmpty(oldToken)) {
//            userInfo.setToken(null);
//            allUsers.evict(oldToken);
//        }
       allUsers.put(userInfo.getUserType() + userInfo.getUsername(),userInfo);
//        String ntk = TokenUtil.token(userInfo.getUserType() + userInfo.getUsername(), userInfo.getPassword(), timeout);
//        long timeExpired = System.currentTimeMillis() / 1000 + timeout;
//        Pair<Long, UserInfo> ud = new Pair<>(timeExpired, userInfo);
//        userInfo.setToken(ntk);
//        allUsers.put(ntk, ud);

        ServletRequestAttributes attributes = getRequestAttributes();
        if(attributes!=null){
            HttpServletRequest request = attributes.getRequest();
            HttpServletResponse response = attributes.getResponse();
           // request.setAttribute(userKeyAttrName, ntk);
            request.setAttribute(userAttrName,userInfo);
           // setToken(ntk, response);
        }
        return userInfo.getPassword() + TokenUtil.getGlobalSalt();
    }

    private static String getCookieName(String userType) {
        return "_t" + userType;
    }

    private static String getTokenName() {
        return "token";
    }

    private static void setToken(String token, HttpServletResponse response) {
        // request.//.(getTokenName(), token);
        response.setHeader(getTokenName(), token);
    }

    private static ServletRequestAttributes getRequestAttributes() {
        return (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
    }
}
